#import "sprite_game_arrow.h"

#import "mgr_game_physics.h"

/*Z ORDER
*/
#define Z_ORDER_ARROW_STREAK 0
#define Z_ORDER_ARROW_IMAGE 1

#define EXPLODE_RADIUS 125

/*sprite_game_arrow
*/
@implementation SpriteGameArrow

@synthesize attackPoint = attackPoint_;
@synthesize perforation = perforation_;
@synthesize defaultRotate = defaultRotate_;
@synthesize movementType = movementType_;
@synthesize parabolaBasePos = parabolaBasePos_;
@synthesize state = state_;
@synthesize flagExplode = flagExplode_;

- (id)initSpriteGameArrow:(int)Type
{
	NSString *_Path;

	if ((self = [super initSpriteGameObject]))
	{
		[self schedule:@selector(tick:)];

		/*base
		*/
		type_ = Type;
		attackPoint_ = g_CfgGameArrowDataTable[type_].attack_point_table[0];
		perforation_ = g_CfgGameArrowDataTable[type_].perforation_table[0];
		level_ = 0;

		/*movement
		*/
		movementType_ = MOVEMENT_TYPE_NONE;
		memset(&parabolaData_, 0, sizeof(TMVParabolaData));
		memset(&linearData_, 0, sizeof(TMVLinearData));
		tickCount_ = 0;

		/*display
		*/
		_Path = [NSString stringWithUTF8String:g_CfgGameArrowDataTable[type_].image_path_table[0].c_str()];
		image_ = [[CCSprite alloc] initWithSpriteFrameName:_Path];
		[self addChild:image_ z:Z_ORDER_ARROW_IMAGE];
		[image_ release];
		image_.positionInPixels = ccp(0.0f, g_CfgGameArrowDataTable[type_].image_size.height / 2.0f);
		switch (type_)
		{
		case ARROW_TYPE_0: break;
		case ARROW_TYPE_1:
		case ARROW_TYPE_2:
		case ARROW_TYPE_3:
		case ARROW_TYPE_4:[self PlayAniNormal]; break;
		default:;
		}
		particle_ = nil;

		/*box2d
		*/
		box2dBody_ = NULL;
		fixtureArrow_ = NULL;
		fixtureExplode_ = NULL;

		/*flag
		*/
		state_ = ARROW_STATE_CONTROL;
		flagExplode_ = false;
		flagExplodeHappened_ = false;
		tickExplodeFrame_ = 0;
	}
	return self;
}

- (void)tick:(ccTime)Time
{
	float _Angle;
	CGPoint _Point;
	b2Vec2 _BodyPoint;

	switch (state_)
	{
	case ARROW_STATE_CONTROL: break;
	case ARROW_STATE_MOVE:
		tickCount_++;
		switch (movementType_)
		{
		case MOVEMENT_TYPE_NONE: return;

		case MOVEMENT_TYPE_LINEAR:
			tmvComputeLinearPoint(&linearData_, tickCount_ * (1.0f / 60.0f), &_Point);
			_Angle = linearData_.angle;
			break;
		case MOVEMENT_TYPE_PARABOLA:
			tmvComputeParabolaPoint(&parabolaData_, tickCount_ * (1.0f / 60.0f), &_Point);
			_Angle = tmvComputeParabolatTangentSlope(&parabolaData_, &_Point);
			_Point.x = parabolaBasePos_.x + _Point.x * g_ParabolaScaleRatio;
			_Point.y = parabolaBasePos_.y + _Point.y * g_ParabolaScaleRatio;
			break;
		default: assert(false);
		}
		[self setPositionInPixels:ccp(_Point.x, _Point.y)];
		[self setRotation:_Angle];
		if (NULL != box2dBody_)
		{
			_BodyPoint = [self computeBodyPosition];
			box2dBody_->SetTransform(_BodyPoint, -1 * CC_DEGREES_TO_RADIANS(self.rotation));
		}
		particle_.rotation = -self.rotation;
		particle_.positionInPixels = ccp(0, g_CfgGameArrowDataTable[type_].image_size.height / 2 - 10);
		particle_.startSpin = self.rotation;
		particle_.startSpinVar = 0;
		particle_.endSpin = 0;
		particle_.endSpinVar = 0;
		break;
	case ARROW_STATE_INVALID: break;
	case ARROW_STATE_EXPLODE: break;
	case ARROW_STATE_EXPLODE_OVER: break;
	default: assert(false);
	}
	if (!uiPointInCenterRect(self.positionInPixels, g_CfgGameArrowValidArea))
	{
		[g_SceneGame.layerObject RemoveArrow:self];
	}

	if (flagExplodeHappened_)
	{
		if (tickExplodeFrame_ == 0)
		{
			state_ = ARROW_STATE_EXPLODE_OVER;
			flagExplodeHappened_ = false;
		}
		else
		{
			tickExplodeFrame_--;
		}
	}
}

- (void)createBody
{
	int i;
	int _Count;
	float _Ratio;
	b2Vec2 *_Vertex;
	b2BodyDef _BodyDef;
	b2PolygonShape _PolygonShape;
	b2FixtureDef _FixtureDef;

	_BodyDef.type = b2_dynamicBody;
	_BodyDef.position = [self computeBodyPosition];
	_BodyDef.angle = -1 * CC_DEGREES_TO_RADIANS(self.rotation);
	box2dBody_ = CMgrGamePhysics::World()->CreateBody(&_BodyDef);

	_Ratio = 1 / CMgrGamePhysics::Ratio();
	_Count = g_CfgGameArrowDataTable[type_].box2d_collision_point_table.size();
	_Vertex = ptCreateCollisionVertex(&g_CfgGameArrowDataTable[type_].box2d_collision_point_table);
	for (i = 0; i < _Count; ++i)
	{
		_Vertex[i] = ptScaleVector_same(_Vertex[i], _Ratio);
	}
	_PolygonShape.Set(_Vertex, _Count);
	free(_Vertex);

	_FixtureDef.shape = &_PolygonShape;
	_FixtureDef.density = 1.0f;
	_FixtureDef.isSensor = true;
	fixtureArrow_ = box2dBody_->CreateFixture(&_FixtureDef);
}

- (void)createExplode
{
	b2CircleShape _CircleShape;
	b2FixtureDef _FixtureDef;

	_CircleShape.m_radius = EXPLODE_RADIUS / CMgrGamePhysics::Ratio();
	_FixtureDef.shape = &_CircleShape;
	_FixtureDef.density = 1.0f;
	_FixtureDef.isSensor = true;
	fixtureExplode_ = box2dBody_->CreateFixture(&_FixtureDef);
}

- (void)CreateStreak
{
	particle_ = [CCParticleSystemQuad particleWithFile:@"arrow_particle.plist"];
	[self addChild:particle_];
}

- (void)RemoveStreak
{
	[self removeChild:particle_ cleanup:TRUE];
	particle_ = nil;
}

- (void)Pause
{
	[self pauseSchedulerAndActions];
	[image_ pauseSchedulerAndActions];
	[particle_ pauseSchedulerAndActions];
}

- (void)Resume
{
	[self resumeSchedulerAndActions];
	[image_ resumeSchedulerAndActions];
	[particle_ resumeSchedulerAndActions];
}

- (float)GetMaxDragLength
{
	return g_CfgGameArrowDataTable[type_].max_drag_distance;
}

- (float)GetLength
{
	return g_CfgGameArrowDataTable[type_].image_size.height;
}

- (float)GetNextLevelHoldTime
{
	return g_CfgGameArrowDataTable[type_].level_up_hold_time_table[level_];
}

- (CGPoint)GetPreLinePosition
{
	CGPoint _Ret;

	_Ret = ptRotatePoint_CGPoint(image_.positionInPixels, -self.rotation);
	_Ret = ccpAdd(self.positionInPixels, _Ret);
	return _Ret;
}

- (void)AddLevel
{
	NSString *_Path;
	CCSpriteFrame *_Frame;

	if (g_CfgGameArrowDataTable[type_].level_max > level_)
	{
		level_++;
		attackPoint_ = g_CfgGameArrowDataTable[type_].attack_point_table[level_];
		perforation_ = g_CfgGameArrowDataTable[type_].perforation_table[level_];
		if (type_ == ARROW_TYPE_0)
		{
			_Path = [NSString stringWithUTF8String:g_CfgGameArrowDataTable[type_].image_path_table[level_].c_str()];
			_Frame = [[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:_Path];
			[image_ setDisplayFrame:_Frame];
		}
	}
}

- (bool)IsMaxLevel
{
	return level_ == g_CfgGameArrowDataTable[type_].level_max;
}

- (bool)IsMyFixtureArrow:(b2Fixture *)Fixture
{
	if (NULL != box2dBody_)
	{
		return fixtureArrow_ == Fixture;
	}
	else
	{} return false;
}

- (bool)IsMyFixtureExplode:(b2Fixture *)Fixture;
{
	if (NULL != box2dBody_)
	{
		return fixtureExplode_ == Fixture;
	}
	else
	{} return false;
}

- (void)ActionInvalid
{
	state_ = ARROW_STATE_INVALID;
	[self StopMovement];
	[self RemoveStreak];
	[self DestroyBody];
	[self PlayAniInvalid];
}

- (void)ActionExplode
{
	state_ = ARROW_STATE_EXPLODE;
	[self StopMovement];
	[self RemoveStreak];
	[self CreateExplode];
	[self PlayAniExplode];
	flagExplodeHappened_ = true;
	tickExplodeFrame_ = 1;
}

- (void)PlayAniNormal
{
	if (type_ != ARROW_TYPE_0)
	{
		[image_ runAction:[CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:uiCreateAnimationObject(&g_CfgGameArrowDataTable[type_].ani_normal)]]];
	}
}

- (void)PlayAniInvalid
{
	id _AniInvalid;
	CCCallFuncN *_CallBackInvalidOver;
	CCSequence *_Seq;

	_AniInvalid = [CCAnimate actionWithAnimation:uiCreateAnimationObject(&g_CfgGameArrowDataTable[type_].ani_invalid)];
	_CallBackInvalidOver = [CCCallFuncN actionWithTarget:self selector:@selector(CallBackAniInvalidOver:)];
	_Seq = [CCSequence actions:_AniInvalid, _CallBackInvalidOver, nil];
	[image_ runAction:_Seq];
}

- (void)PlayAniExplode
{
	id _AniInvalid;
	CCCallFuncN *_CallBackExplodeOver;
	CCSequence *_Seq;

	_AniInvalid = [CCAnimate actionWithAnimation:uiCreateAnimationObject(&g_CfgGameArrowDataTable[type_].ani_explode)];
	_CallBackExplodeOver = [CCCallFuncN actionWithTarget:self selector:@selector(CallBackAniExplodeOver:)];
	_Seq = [CCSequence actions:_AniInvalid, _CallBackExplodeOver, nil];
	[image_ runAction:_Seq];
}

- (void)StartParabolaMovement:(CGPoint)BasePos And:(CGPoint)Speed
{
	CGPoint _BasePos;

	state_ = ARROW_STATE_MOVE;

	image_.positionInPixels = CGPointZero;
	_BasePos = ccp(0.0f, g_CfgGameArrowDataTable[type_].image_size.height);
	_BasePos = ptRotatePoint_CGPoint(_BasePos, -self.rotation);
	_BasePos = ccpAdd(_BasePos, BasePos);

	movementType_ = MOVEMENT_TYPE_PARABOLA;
	parabolaData_.start_speed = Speed;
	parabolaBasePos_ = _BasePos;
	tmvComputeParabola(&parabolaData_);
	tickCount_ = 0;

	[self CreateStreak];
}

- (void)StartLinearMovement:(CGPoint)StartPos And:(CGPoint)EndPos And:(float)Speed
{
	CGPoint _BasePos;

	state_ = ARROW_STATE_MOVE;

	image_.positionInPixels = CGPointZero;
	_BasePos = ccp(0.0f, g_CfgGameArrowDataTable[type_].image_size.height);
	_BasePos = ptRotatePoint_CGPoint(_BasePos, -self.rotation);
	_BasePos = ccpAdd(_BasePos, StartPos);

	movementType_ = MOVEMENT_TYPE_LINEAR;
	linearData_.start_pos = _BasePos;
	linearData_.end_pos = EndPos;
	linearData_.base_speed = Speed;
	linearData_.flag_infinity = true;
	tmvComputeLinear(&linearData_);
	tickCount_ = 0;

	[self CreateStreak];
}

- (void)StopMovement
{
	movementType_ = MOVEMENT_TYPE_NONE;
	memset(&parabolaData_, 0, sizeof(TMVParabolaData));
	memset(&linearData_, 0, sizeof(TMVLinearData));
	tickCount_ = 0;
}

- (void)CallBackAniInvalidOver:(id)Sender
{
	[g_LayerGameObject RemoveArrow:self];
}

- (void)CallBackAniExplodeOver:(id)Sender
{
	[g_LayerGameObject RemoveArrow:self];
}

@end
